﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using WINBASE.Utils;

namespace WINBASE.Controls
{
	[Description("DataGridView that Saves Column Order, Width and Visibility to user.config")]
	[ToolboxBitmap(typeof(System.Windows.Forms.DataGridView))]
	public class WLCDataGridView : DataGridView
	{
        private DataRetriever myDataRetriever;
        private List<DataGridViewColumn> primaryColumns;
        private List<DataGridViewColumn> orderColumns;
        
        int oldScrollValue = 0;
        bool IsSystemMode = false;
        bool IsScrollDone = false;

        #region Event

        public WLCDataGridView()
        {
            InitializeComponent();
            this.BorderStyle = BorderStyle.None;
        }

        private void JitDataGrid_Scroll(object sender, ScrollEventArgs e)
        {
            if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
            {
                if (this.Rows.Count < this.TotalRowCount)
                {
                    if (IsScrollDone == false)
                    {
                        oldScrollValue = e.NewValue;
                        IsScrollDone = true;
                    }
                    else
                    {
                        e.NewValue = oldScrollValue;
                        IsScrollDone = false;
                    }
                }
            }
        }

        private void JitDataGrid_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e)
        {
            if (!IsSystemMode)
            {
                if (e.StateChanged == DataGridViewElementStates.Displayed)
                {
                    if (e.Row.Index == this.RowCount - 6)
                    {
                        if ((e.Row.Visible))
                        {
                            if (this.Rows.Count < this.TotalRowCount)
                            {
                                int PrimaryKeyValue = Convert.ToInt32(this.Rows[this.Rows.Count - 1].Cells[this.PrimaryKey].Value);
                                DataTable dtNew = myDataRetriever.SupplyPageOfData(PrimaryKey, PrimaryKeyValue, RowsPerPage);
                                IsScrollDone = true;
                                ((DataTable)this.DataSource).Merge(dtNew, false);
                            }
                        }
                    }
                }
            }
        } 

        #endregion

        #region Method

        private void InitializeComponent()
        {
            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
            ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
            this.SuspendLayout();
            // 
            // WLCDataGridView
            // 
            this.AllowUserToAddRows = false;
            this.AllowUserToDeleteRows = false;
            this.AllowUserToOrderColumns = true;
            this.AllowUserToResizeRows = false;
            this.BackgroundColor = System.Drawing.SystemColors.Window;
            this.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None;
            dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
            dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
            dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
            dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
            dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
            dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
            this.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
            this.ColumnHeadersHeight = 28;
            this.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
            this.Dock = System.Windows.Forms.DockStyle.Fill;
            this.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically;
            this.GridColor = System.Drawing.SystemColors.Window;
            this.Location = new System.Drawing.Point(1, 0);
            this.MultiSelect = false;
            this.RowHeadersWidth = 25;
            this.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
            this.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
            this.ShowCellErrors = false;
            this.ShowCellToolTips = false;
            this.ShowEditingIcon = false;
            this.ShowRowErrors = false;
            this.Size = new System.Drawing.Size(805, 376);
            this.StandardTab = true;
            this.DataBindingComplete += new System.Windows.Forms.DataGridViewBindingCompleteEventHandler(this.WLCDataGridView_DataBindingComplete);
            this.RowStateChanged += new System.Windows.Forms.DataGridViewRowStateChangedEventHandler(this.JitDataGrid_RowStateChanged);
            this.Scroll += new System.Windows.Forms.ScrollEventHandler(this.JitDataGrid_Scroll);
            ((System.ComponentModel.ISupportInitialize)(this)).EndInit();
            this.ResumeLayout(false);

        }

        public void InitializeData()
        {
            IsSystemMode = true;
            if ((ViewName ?? "") != "")
            {
                AddColumns();
                SetColumnSetting();
                SetDataSource();
            }
            else
            {
                SetDataSource();
            }
            IsSystemMode = false;
        }

        public void RefreshData()
        {
            IsSystemMode = true;
            SetDataSource();
            IsSystemMode = false;
        }

        public void SetColumnSetting()
        {
            _DisplayColumns = new List<DataGridViewColumn>();
            orderColumns = new List<DataGridViewColumn>();

            if (gfDataGridViewSetting.Default.ColumnSetting.ContainsKey(this.Name))
            {
                List<ColumnSettingItem> columnSetting = gfDataGridViewSetting.Default.ColumnSetting[this.Name];
                if (columnSetting != null)
                {
                    if ((columnSetting.Count <= this.Columns.Count))
                    {
                        dynamic sorted = columnSetting.OrderBy(i => i.DisplayIndex);
                        foreach (ColumnSettingItem Item in sorted)
                        {
                            this.Columns[Item.ColumnIndex].DisplayIndex = Item.DisplayIndex;
                            this.Columns[Item.ColumnIndex].Visible = Item.Visible;
                            this.Columns[Item.ColumnIndex].Width = Item.Width;
                            this.Columns[Item.ColumnIndex].SortMode = DataGridViewColumnSortMode.Automatic;
                            if (Item.IsSortedColumn)
                            {
                                ListSortDirection SortDirection = default(ListSortDirection);
                                switch (Item.SortOrder)
                                {
                                    case SortOrder.Ascending:
                                        SortDirection = ListSortDirection.Ascending;
                                        break; // TODO: might not be correct. Was : Exit Select
                                    default:
                                        SortDirection = ListSortDirection.Descending;
                                        break; // TODO: might not be correct. Was : Exit Select
                                }
                                if (this.Columns[Item.ColumnIndex].IsDataBound)
                                {
                                    this.Sort(this.Columns[Item.ColumnIndex], SortDirection);
                                }
                                orderColumns.Add(this.Columns[Item.ColumnIndex]);
                            }

                            if (Item.Visible && this.Columns[Item.ColumnIndex].DataPropertyName != "")
                            {
                                _DisplayColumns.Add(this.Columns[Item.ColumnIndex]);
                            }
                        }
                    }
                }
            }
            else
            {
                foreach (DataGridViewColumn dgvCol in this.Columns)
                {
                    if (dgvCol.Visible && dgvCol.DataPropertyName != "")
                    {
                        _DisplayColumns.Add(dgvCol);
                    }
                }
            }
        }

        public void SaveColumnOrder()
        {
            List<ColumnSettingItem> columnOrder = new List<ColumnSettingItem>();
            DataGridViewColumnCollection columns = this.Columns;
            for (int i = 0; i < columns.Count; i++)
            {
                columnOrder.Add(new ColumnSettingItem
                {
                    ColumnIndex = i,
                    DisplayIndex = columns[i].DisplayIndex,
                    Visible = columns[i].Visible,
                    Width = columns[i].Width,
                    IsSortedColumn = (this.SortedColumn == columns[i]),
                    SortOrder = this.SortedColumn == columns[i] ? this.SortOrder : SortOrder.None
                });
            }
            gfDataGridViewSetting.Default.ColumnSetting[this.Name] = columnOrder;
            gfDataGridViewSetting.Default.Save();
        }

        private void AddColumns()
        {
            if (this.InvokeRequired)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    AddColumns();
                    return;
                });
            }
            else
            {
                if ((this.ViewName ?? "") == "") return;

                primaryColumns = new List<DataGridViewColumn>();
                this.AutoGenerateColumns = false;
                this.Columns.Clear();

                if (!string.IsNullOrEmpty(_ViewName))
                {
                    TotalRowCount = (int)ApplicationService.QueryHelper.GetAVal("SELECT COUNT(*) FROM " + _ViewName);
                    DataTable dtColumn = ApplicationService.QueryHelper.GetASchemaDataTable(_ViewName);

                    int i = 0;
                    foreach (DataRow drcol in dtColumn.Rows)
                    {
                        DataGridViewColumn mydgvCol = new DataGridViewTextBoxColumn();
                        mydgvCol.Name = drcol["COLUMN_NAME"].ToString();
                        mydgvCol.HeaderText = drcol["COLUMN_NAME"].ToString();
                        mydgvCol.DataPropertyName = drcol["COLUMN_NAME"].ToString();

                        if ((drcol["TYPE_NAME"].ToString().Contains("identity")))
                        {
                            primaryColumns.Add(mydgvCol);
                            if ((string.IsNullOrEmpty(_PrimaryKey)))
                            {
                                _PrimaryKey = mydgvCol.Name;
                            }
                        }
                        string strSqlType = drcol["TYPE_NAME"].ToString().Replace("identity", "").Trim();
                        SqlDbType SqlType = (SqlDbType)Enum.Parse(typeof(SqlDbType), strSqlType, true);
                        bool IsNullable = drcol["NULLABLE"].ToString() == "1" ? true : false;
                        Type colValueType = GetClrType(SqlType, IsNullable);
                        mydgvCol.ValueType = colValueType;

                        this.Columns.Add(mydgvCol);

                        i += 1;
                        if (i > this._NoOfMaxColumns)
                        {
                            break; // TODO: might not be correct. Was : Exit For
                        }
                    }

                    DataGridViewColumn mydgvPlacerCol = new DataGridViewTextBoxColumn();
                    mydgvPlacerCol.Name = " ";
                    mydgvPlacerCol.HeaderText = "";
                    mydgvPlacerCol.ValueType = typeof(string);
                    mydgvPlacerCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                    mydgvPlacerCol.Resizable = DataGridViewTriState.False;
                    mydgvPlacerCol.SortMode = DataGridViewColumnSortMode.Programmatic;
                    this.Columns.Add(mydgvPlacerCol);
                }
                return;
            }
        }

        private void SetDataSource()
        {
            //Select Data From View
            if ((this.ViewName ?? "") != "")
            {
                //Select Columns List from datagridview display columns
                string commasepcolumns = "";
                if (_DisplayColumns != null && _DisplayColumns.Count > 0)
                {
                    foreach (DataGridViewColumn col in _DisplayColumns)
                    {
                        commasepcolumns += (string.IsNullOrEmpty(commasepcolumns) ? string.Empty : ", ") + '[' + col.DataPropertyName + ']';
                    }
                }
                else commasepcolumns = "*";

                //Order By Columns List
                List<string> orderColNames = new List<string>();

                //1. Add Primary Key from developer
                if ((this.PrimaryKey ?? "") != "")
                {
                    orderColNames.Add(this.PrimaryKey);
                }

                //2. Add User defined sorted columns
                foreach (DataGridViewColumn colOrder in orderColumns)
                {
                    if (!orderColNames.Contains(colOrder.DataPropertyName))
                    {
                        orderColNames.Add(colOrder.DataPropertyName);
                    }
                }

                //Select Data
                DataTable dt;
                myDataRetriever = new DataRetriever(_ViewName, commasepcolumns, this.Filter, null, orderColNames);
                if ((this.PrimaryKey ?? "") != "")
                {
                    dt = myDataRetriever.SupplyPageOfData(primaryColumns[0].DataPropertyName, 0, RowsPerPage);
                }
                else
                {
                    dt = myDataRetriever.SupplyAllData();
                    dt.DefaultView.RowFilter = this.Filter;
                }
                this.DataSource = dt;
            }
            else //Select Data from Datatable
            {
                ((DataTable)this.DataSource).DefaultView.RowFilter = this.Filter;
            }
        }

        public Type GetClrType(SqlDbType SqlType, bool IsNullable)
        {
            switch (SqlType)
            {
                case SqlDbType.BigInt:

                    return IsNullable ? typeof(System.Nullable<long>) : typeof(long);
                case SqlDbType.Binary:
                case SqlDbType.Image:
                case SqlDbType.Timestamp:
                case SqlDbType.VarBinary:

                    return typeof(byte[]);
                case SqlDbType.Bit:

                    return IsNullable ? typeof(System.Nullable<bool>) : typeof(bool);
                case SqlDbType.Char:
                case SqlDbType.NChar:
                case SqlDbType.NText:
                case SqlDbType.NVarChar:
                case SqlDbType.Text:
                case SqlDbType.VarChar:
                case SqlDbType.Xml:

                    return typeof(string);
                case SqlDbType.DateTime:
                case SqlDbType.SmallDateTime:
                case SqlDbType.Date:
                case SqlDbType.Time:
                case SqlDbType.DateTime2:

                    return IsNullable ? typeof(System.Nullable<DateTime>) : typeof(DateTime);
                case SqlDbType.Decimal:
                case SqlDbType.Money:
                case SqlDbType.SmallMoney:

                    return IsNullable ? typeof(System.Nullable<decimal>) : typeof(decimal);
                case SqlDbType.Float:

                    return IsNullable ? typeof(System.Nullable<double>) : typeof(double);
                case SqlDbType.Int:

                    return IsNullable ? typeof(System.Nullable<int>) : typeof(int);
                case SqlDbType.Real:

                    return IsNullable ? typeof(System.Nullable<float>) : typeof(float);
                case SqlDbType.UniqueIdentifier:

                    return IsNullable ? typeof(System.Nullable<Guid>) : typeof(Guid);
                case SqlDbType.SmallInt:

                    return IsNullable ? typeof(System.Nullable<short>) : typeof(short);
                case SqlDbType.TinyInt:

                    return IsNullable ? typeof(System.Nullable<byte>) : typeof(byte);
                case SqlDbType.Variant:
                case SqlDbType.Udt:

                    return typeof(object);
                case SqlDbType.Structured:

                    return typeof(DataTable);
                case SqlDbType.DateTimeOffset:
                    return IsNullable ? typeof(System.Nullable<DateTimeOffset>) : typeof(DateTimeOffset);
                default:

                    throw new ArgumentOutOfRangeException("sqlType");
            }
        }

        protected override void Dispose(bool disposing)
        {
            SaveColumnOrder();
            base.Dispose(disposing);
        }

        #endregion

        #region Property

        private Boolean _DataPaginated = true;
        [Category("WINBASE"), DefaultValue(true), Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Boolean DataPaginated
        {
            get { return _DataPaginated; }
            set { _DataPaginated = value; }
        }

        private string _ViewName = "";
        [Category("WINBASE"),Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public string ViewName {
	        get { return _ViewName; }
	        set { _ViewName = value; }
        }

        [Category("WINBASE"), Browsable(true), DefaultValue(""), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string Filter { get; set; }

        private int _RowsPerPage = 50;
        [Category("WINBASE"), DefaultValue("50"), Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public int RowsPerPage {
	        get { return _RowsPerPage; }
            set { _RowsPerPage = value; }
        }

        [Category("WINBASE"), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int TotalRowCount { get; set; }

        private int _NoOfMaxColumns = 50;
        [Category("WINBASE"), DefaultValue(50), Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int NoOfMaxColumns
        {
            get { return _NoOfMaxColumns; }
            set { _NoOfMaxColumns = value; }
        }

        private string _PrimaryKey = "";
        [Category("WINBASE"), Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public string PrimaryKey
        {
            get { return _PrimaryKey; }
            set { _PrimaryKey = value; }
        }

        private List<DataGridViewColumn> _DisplayColumns;
        [Category("WINBASE"),Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public List<DataGridViewColumn> DisplayColumns
        {
            get { return _DisplayColumns; }
            set { _DisplayColumns = value; }
        }

        #endregion

        private void WLCDataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            if (this.Columns["Status Code"] != null)
            {
                foreach (DataGridViewRow dgr in this.Rows)
                {
                    if (dgr.Cells["Status Code"].Value.ToString() != "")
                    {
                        if (dgr.Cells["Status Code"].Value.ToString() == "DEL")
                        {
                            dgr.DefaultCellStyle.ForeColor = Color.Red;
                            dgr.DefaultCellStyle.Font = new Font(this.Font, FontStyle.Bold);
                        }
                    }
                }    
            }
        }
	}
	//-------------------------------------------------------------------------
	internal sealed class gfDataGridViewSetting : ApplicationSettingsBase
	{
		private static gfDataGridViewSetting _defaultInstance =
			(gfDataGridViewSetting)ApplicationSettingsBase
			.Synchronized(new gfDataGridViewSetting());
		//---------------------------------------------------------------------
		public static gfDataGridViewSetting Default
		{
			get { return _defaultInstance; }
		}
		//---------------------------------------------------------------------
		// Because there can be more than one DGV in the user-application
		// a dictionary is used to save the settings for this DGV.
		// As key the name of the control is used.
		[UserScopedSetting]
		[SettingsSerializeAs(SettingsSerializeAs.Binary)]
		[DefaultSettingValue("")]
		public Dictionary<string, List<ColumnSettingItem>> ColumnSetting
		{
			get { return this["ColumnSetting"] as Dictionary<string, List<ColumnSettingItem>>; }
			set { this["ColumnSetting"] = value; }
		}
	}
	//-------------------------------------------------------------------------
	[Serializable]
	public sealed class ColumnSettingItem
	{
		public int DisplayIndex { get; set; }
		public int Width { get; set; }
		public bool Visible { get; set; }
		public int ColumnIndex { get; set; }
        public bool IsSortedColumn { get; set; }
        public SortOrder SortOrder { get; set; }
	}

    public class DataRetriever
    {
        private string tableName;
        private string commasepcolumns;
        private string range;
        private List<object> rangeargs;

        private List<string> sortby;
        public DataRetriever(string TableName, string CommaSeparatedColumns, string Range, List<object> Rangeargs, List<string> SortBy)
        {
            this.tableName = TableName;
            this.commasepcolumns = CommaSeparatedColumns;
            this.range = Range;
            this.rangeargs = Rangeargs;
            this.sortby = SortBy;
        }

        public DataTable SupplyAllData()
        {
            string @orderby = sortby.ToString(", ", "[", "]");
            string strQuery = "SELECT " + commasepcolumns + " " +
                              "FROM " + tableName + " " +
                              (string.IsNullOrEmpty(range) ? "" : "WHERE ") + range + " " +
                              (string.IsNullOrEmpty(@orderby) ? "" : "ORDER BY " + @orderby);

            DataTable table = ApplicationService.QueryHelper.GetADataTable(strQuery);
            return table;
        }

        public DataTable SupplyPageOfData(string ViewKey, int KeyValue, int rowsPerPage)
        {
            string @orderby = sortby.ToString(", ", "[", "]");
            string strQuery = "SELECT TOP " + rowsPerPage.ToString() + " " + commasepcolumns + " " +
                              "FROM " + tableName + " " + 
                              "WHERE [" + ViewKey + "] > @s " + (string.IsNullOrEmpty(range) ? "" : "AND ") + range + " " + 
                              (string.IsNullOrEmpty(@orderby) ? "" : "ORDER BY " + @orderby);

            DataTable table =  ApplicationService.QueryHelper.GetADataTable(strQuery, KeyValue);
            return table;
        }
    }
}